import numpy as np
from random import shuffle, randint, uniform
import matplotlib.pyplot as plt


class HGA:
    def __init__(self):
        self.data = None
        self.Y_width = None
        self.X_width = None
        self.n = None
        self.NP = 20

    def read_tsp_instance(self, path):
        self.data = np.loadtxt(path, skiprows=6, encoding='utf-8', dtype=float)
        self.X_width = self.data[:, 1].max() - self.data[:, 1].min()
        self.Y_width = self.data[:, 2].max() - self.data[:, 2].min()
        self.n = self.data.shape[0]
        self.data = np.insert(self.data, obj=0, values=0, axis=0)

    def draw(self, plot, route=None, show_text=False, dot_size=None):
        dot_size = round(np.log2(self.n)) * 4 if dot_size is None else dot_size
        plot.scatter(self.data[1:, 1], self.data[1:, 2], s=dot_size, c='#3e4145')
        if show_text:
            for data in self.data[1:]:
                plot.text(x=data[1], y=data[2], s=int(data[0]))
        if route is not None:
            plot.plot(self.data[:, 1][route], self.data[:, 2][route])
        plot.axis('equal')
        return plot

    def optimize(self):
        # random init
        population = [list(range(1, self.n + 1)) for _ in range(self.NP)]
        for individual in population:
            shuffle(individual)
        # zoning crossover
        for i in range(1, self.NP):
            population[i - 1], population[i] = self.zoning_cross(population[i - 1], population[i])

    def zoning_cross(self, p1, p2):
        center = randint(1, self.n)
        scaling = uniform(0.1, 0.3)
        L_x, L_y = self.X_width * scaling, self.Y_width * scaling
        inside, outside = [], []
        for data in self.data[1:]:
            if abs(self.data[center, 1] - data[1]) < L_x / 2 and abs(self.data[center, 2] - data[2]) < L_y / 2:
                inside.append(int(data[0]))
            else:
                outside.append(int(data[0]))
        # plot = self.draw(plt)
        # start_point = (self.data[center, 1] - L_x / 2, self.data[center, 2] - L_y / 2)
        # plot.scatter(self.data[inside, 1], self.data[inside, 2], c='red')
        # plot.scatter(self.data[outside, 1], self.data[outside, 2], c='blue')
        # plot.gca().add_patch(plt.Rectangle(start_point, L_x, L_y, color='green', fill=False))
        # plot.show()
        return p1, p2


optimizer = HGA()
optimizer.read_tsp_instance(path=f'../../data/TSPLIB/att48.tsp.txt')
optimizer.optimize()


